package main

import (
	"crypto/sha256"
	"fmt"
	"math/big"
	"strconv"
	"time"
)

// Block represents a block in the blockchain
type Block struct {
	Timestamp int64
	Data      string
	Previous  string
	Hash      string
	Nonce     int
}

// ProofOfWork represents the proof of work algorithm
type ProofOfWork struct {
	Block  *Block
	Target *big.Int
}

// Difficulty level of the proof of work algorithm
const difficulty = 4

// NewProofOfWork creates a new proof of work
func NewProofOfWork(b *Block) *ProofOfWork {
	target := big.NewInt(1)
	target.Lsh(target, 256-difficulty) // Adjust difficulty
	return &ProofOfWork{Block: b, Target: target}
}

// Run performs the proof of work algorithm
// Run performs the proof of work algorithm
func (pow *ProofOfWork) Run() (int, string) {
	var hashInt big.Int
	nonce := 0
	var hash [32]byte // 将 hash 变量声明移到循环外部

	for {
		data := strconv.FormatInt(pow.Block.Timestamp, 10) +
			pow.Block.Data + pow.Block.Previous + strconv.Itoa(nonce)
		hash = sha256.Sum256([]byte(data))

		hashInt.SetBytes(hash[:])

		if hashInt.Cmp(pow.Target) <= 0 {
			fmt.Printf("Hash found! %x\n", hash)
			break
		} else {
			nonce++
		}
	}

	pow.Block.Nonce = nonce
	pow.Block.Hash = fmt.Sprintf("%x", hash) // 使用循环外部的 hash 变量
	return nonce, pow.Block.Hash
}

// Validate checks if the block's hash is valid
func (pow *ProofOfWork) Validate() bool {
	var hashInt big.Int

	data := strconv.FormatInt(pow.Block.Timestamp, 10) +
		pow.Block.Data + pow.Block.Previous + strconv.Itoa(pow.Block.Nonce)
	hash := sha256.Sum256([]byte(data))
	hashInt.SetBytes(hash[:])

	return hashInt.Cmp(pow.Target) <= 0
}

// Main function to create a new block and run the proof of work
func main() {
	// Create a new block
	block := Block{
		Timestamp: time.Now().Unix(),
		Data:      "First Block in the chain",
		Previous:  "0",
	}

	// Create a new proof of work
	pow := NewProofOfWork(&block)

	// Run the proof of work algorithm
	nonce, hash := pow.Run()

	// Print the result
	fmt.Printf("Nonce: %d\n", nonce)
	fmt.Printf("Hash: %s\n", hash)

	// Validate the block
	if pow.Validate() {
		fmt.Println("Block is valid!")
	} else {
		fmt.Println("Block is invalid!")
	}
}